
-- dont mind the weird gapes, i fucked up while doing a ctrl c + ctrl v between 2 saves
--posez pas de questions sur les espaces chelous entre les lignes, j'ai merdé sur un copier coller entre 2 saves

-- Set up



local TARGET_MAP = "MAP40"
local function is_correct_map()
    return (G_BuildMapName(gamemap) == TARGET_MAP)
end
freeslot(
    "MT_AMAL_ENEMY", "S_AMAL_LOOK", "S_AMAL_CHASE", "S_AMAL_ROAM", "S_AMAL_ATTACK",
    "SPR_AMAL", "SPR_AMAN", "MT_AMAL_PROJ", "S_AMAL_PROJ", "SPR_ALHD"
)

-- Paramètres de l'amalgame

local ROAM_SPEED = 4 * FRACUNIT 
local CHASE_SPEED = 14 * FRACUNIT
local STEP_THRESHOLD = 24 * FRACUNIT


-- Définition des objets

mobjinfo[MT_AMAL_ENEMY] = {
    doomednum = 3740, spawnstate = S_AMAL_LOOK, spawnhealth = 1000,
    radius = 24*FRACUNIT, height = 32*FRACUNIT, speed = 14, damage = 1,
    mass = 100, flags = MF_PAIN|MF_SPECIAL|MF_SLIDEME

}

states[S_AMAL_LOOK] = {SPR_AMAL, A, 5, nil, 0, 0, S_AMAL_LOOK}
states[S_AMAL_ROAM] = {SPR_AMAL, A|FF_ANIMATE, -1, nil, 1, 8, S_AMAL_ROAM}
states[S_AMAL_CHASE] = {SPR_AMAL, A|FF_ANIMATE, 4, nil, 1, 2, S_AMAL_CHASE}
states[S_AMAL_ATTACK] = {SPR_AMAN, A|FF_ANIMATE, 18, nil, 5, 3, S_AMAL_CHASE}


mobjinfo[MT_AMAL_PROJ] = {
    doomednum = -1, spawnstate = S_AMAL_PROJ, radius = 12*FRACUNIT, height = 12*FRACUNIT,
    speed = 0, damage = 1, flags = MF_MISSILE|MF_NOGRAVITY|MF_NOBLOCKMAP
}

states[S_AMAL_PROJ] = {SPR_ALHD, A|FF_ANIMATE, -1, nil, 7, 2, S_AMAL_PROJ}


local function canMoveSafely(mo, angle, speed)
    local checkDist = mo.radius + speed + (10*FRACUNIT)
    local x = mo.x + FixedMul(checkDist, cos(angle))
    local y = mo.y + FixedMul(checkDist, sin(angle))
    local floorz = P_FloorzAtPos(x, y, mo.z, mo.height)
    if (mo.floorz - floorz) > STEP_THRESHOLD then return false end
    return true
end


-- La gestion des timers et des joueurs

addHook("PlayerThink", function(player)
    if player.amalTag and player.amalTag > 0 then
        player.amalTag = $ - 1
    end

end)




-- Le comportement principal de l'amalgame (c'est là ou ça part en couille un peu)



addHook("MobjThinker", function(mo)

    if not is_correct_map() then return end 
    if not (mo and mo.valid) or mo.type ~= MT_AMAL_ENEMY then return end

    for player in players.iterate do
        if player.mo and player.mo.valid and player.mo.health > 0 then
            local dist = P_AproxDistance(mo.x - player.mo.x, mo.y - player.mo.y)

            if dist < (mo.radius + player.mo.radius) and abs(mo.z - player.mo.z) < mo.height then
                if player.amalTag and player.amalTag > 0 then

                    player.powers[pw_flashing] = 0
                    P_DamageMobj(player.mo, mo, mo, 10000)
                    player.amalTag = 0

                elseif player.powers[pw_flashing] == 0 then
                    P_DamageMobj(player.mo, mo, mo, 1)

                end
            end
        end
    end


    if mo.lostTimer == nil then mo.lostTimer = 0 end
    if mo.shotTimer == nil then mo.shotTimer = 0 end 
    if mo.roamTimer == nil then mo.roamTimer = 0 end
    if mo.roamDelay == nil then mo.roamDelay = 0 end 
    if mo.lastX == nil then mo.lastX = mo.x end
    if mo.lastY == nil then mo.lastY = mo.y end
    if mo.shotTimer > 0 then mo.shotTimer = $ - 1 end


    local foundTarget = false

    if mo.target and mo.target.valid and mo.target.health > 0 then

        if P_CheckSight(mo, mo.target) then mo.lostTimer = 4 * TICRATE; foundTarget = true

        elseif mo.lostTimer > 0 then mo.lostTimer = $ - 1; foundTarget = true end

    else

        for player in players.iterate do

            if player.mo and player.mo.valid and player.mo.health > 0 then

                if P_CheckSight(mo, player.mo) and P_AproxDistance(mo.x-player.mo.x, mo.y-player.mo.y) < 1600*FRACUNIT then

                    mo.target = player.mo; foundTarget = true; break

                end

            end

        end

    end


    if foundTarget then

        mo.roamTimer = 0; mo.roamDelay = 0

        local dist = P_AproxDistance(mo.x - mo.target.x, mo.y - mo.target.y)

        if P_CheckSight(mo, mo.target) and dist < 1600*FRACUNIT and mo.shotTimer == 0 then
            mo.state = S_AMAL_ATTACK
            local proj = P_SpawnMobj(mo.x, mo.y, mo.z + 16*FRACUNIT, MT_AMAL_PROJ)
            if proj and proj.valid then proj.target = mo.target; proj.tracer = mo end

            mo.shotTimer = 3 * TICRATE

        end

        if mo.state ~= S_AMAL_ATTACK then

            if mo.state ~= S_AMAL_CHASE then mo.state = S_AMAL_CHASE end
            mo.angle = R_PointToAngle2(mo.x, mo.y, mo.target.x, mo.target.y)
            if canMoveSafely(mo, mo.angle, CHASE_SPEED) then P_InstaThrust(mo, mo.angle, CHASE_SPEED)
            else mo.momx, mo.momy = 0, 0 end

        end

    else

        mo.momx = FixedMul($, 20*FRACUNIT/32); mo.momy = FixedMul($, 20*FRACUNIT/32)

        if mo.roamDelay > 0 then

            mo.roamDelay = $ - 1

            if mo.state ~= S_AMAL_LOOK then mo.state = S_AMAL_LOOK end

        else

            local distActual = P_AproxDistance(mo.x - mo.lastX, mo.y - mo.lastY)
            local isStuck = (mo.state == S_AMAL_ROAM and (distActual < (1*FRACUNIT + FRACUNIT/2) or not canMoveSafely(mo, mo.angle, ROAM_SPEED)))

            if mo.roamTimer <= 0 or isStuck then

                if isStuck then mo.angle = $ + (P_RandomRange(120, 240) * ANG1); mo.roamDelay = 15
                else mo.angle = P_RandomRange(0, 359) * ANG1; mo.roamDelay = P_RandomRange(1, 3) * TICRATE end
                mo.roamTimer = P_RandomRange(3, 6) * TICRATE

            end

            if mo.roamDelay == 0 then

                mo.roamTimer = $ - 1
                if mo.state ~= S_AMAL_ROAM then mo.state = S_AMAL_ROAM end
                if canMoveSafely(mo, mo.angle, ROAM_SPEED) then P_InstaThrust(mo, mo.angle, ROAM_SPEED)
                else mo.momx, mo.momy = 0, 0 end

            end

        end

    end

    mo.lastX = mo.x; mo.lastY = mo.y

end, MT_AMAL_ENEMY)




-- le moment ou il balance son cranium sur toi



addHook("MobjThinker", function(mo)

    if not is_correct_map() then return end 
    if not (mo and mo.valid and mo.type == MT_AMAL_PROJ) then return end
    if not (mo.target and mo.target.valid) then P_RemoveMobj(mo) return end

    mo.angle = R_PointToAngle2(mo.x, mo.y, mo.target.x, mo.target.y)

    P_InstaThrust(mo, mo.angle, 16*FRACUNIT)

    mo.momz = ((mo.target.z + mo.target.height/2) - mo.z) / 10

    if P_AproxDistance(mo.x-mo.target.x, mo.y-mo.target.y) < 32*FRACUNIT then
        if mo.target.player then mo.target.player.amalTag = 2 * TICRATE end
        P_DamageMobj(mo.target, mo, mo.tracer, 1)
        P_RemoveMobj(mo)

    end

end, MT_AMAL_PROJ)




-- les messages un peu flippant pour que les joueurs se posent des questions



addHook("MobjDamage", function(target, inflictor, source, damage)

    if not is_correct_map() then return end

    

    if (source and source.type == MT_AMAL_ENEMY) or (inflictor and inflictor.type == MT_AMAL_PROJ) then

        if target.player then

            local has_shield = (target.player.powers[pw_shield] ~= 0)
            local has_rings = (target.player.rings > 0)
            local is_combo = (damage >= 1000)


            -- le combo (on tag le player quand il se prend un cranium): 

            -- On ne meurt que si (C'est un combo ET qu'on n'a ni bouclier ni rings)

            -- OU si (On n'a plus rien du tout)

            if (is_combo or not (has_shield or has_rings)) 

            and not (has_shield or has_rings) then

                print(target.player.name .. " was killed by ????")
                P_KillMobj(target, inflictor, source)

                return true 

            end

        end

    end

end, MT_PLAYER)




-- la musique quand il est agro


local isChasePlaying = false

local musique_active = true 

local timer_desactivation = 210 * TICRATE 


addHook("MapLoad", function()

    isChasePlaying = false; musique_active = true; timer_desactivation = 210 * TICRATE

    for player in players.iterate do 

        player.amalTag = 0 

        player.killedByAmal = false

    end

end)


addHook("ThinkFrame", function()

    if not is_correct_map() or not musique_active then isChasePlaying = false; return end

    if timer_desactivation > 0 then timer_desactivation = $ - 1

    else

        if isChasePlaying then S_ChangeMusic(mapmusname, true, consoleplayer) end

        musique_active = false; isChasePlaying = false; return

    end

    if not (consoleplayer and consoleplayer.valid and consoleplayer.mo) then return end

    local beingChased = false

    for mo in mobjs.iterate(MT_AMAL_ENEMY) do

        if mo.target and mo.target == consoleplayer.mo and (mo.lostTimer or 0) > 0 then

            beingChased = true; break

        end

    end

    if beingChased then

        if S_MusicName() ~= "AMALM" then S_ChangeMusic("AMALM", true, consoleplayer); isChasePlaying = true end

    else

        if isChasePlaying then S_ChangeMusic(mapmusname, true, consoleplayer); isChasePlaying = false end

    end

end) 